package utils

import (
	"encoding/json"
	"fmt"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)

// AuthConfigManager manages dynamic authentication configuration
type AuthConfigManager struct {
	config *DynamicAuthConfig
	mutex  sync.RWMutex
	
	// Configuration change callbacks
	callbacks []func(*DynamicAuthConfig)
	
	// Environment variable mappings
	envMappings map[string]string
}

// NewAuthConfigManager creates a new configuration manager
func NewAuthConfigManager() *AuthConfigManager {
	manager := &AuthConfigManager{
		config:      GetDefaultDynamicAuthConfig(),
		callbacks:   make([]func(*DynamicAuthConfig), 0),
		envMappings: getEnvironmentMappings(),
	}
	
	// Load configuration from environment variables
	manager.loadFromEnvironment()
	
	return manager
}

// getEnvironmentMappings returns the mapping between config fields and environment variables
func getEnvironmentMappings() map[string]string {
	return map[string]string{
		"enabled":                     "MCP_DYNAMIC_AUTH_ENABLED",
		"max_auth_states_per_session": "MCP_AUTH_MAX_STATES_PER_SESSION",
		"token_expiration_buffer":     "MCP_AUTH_TOKEN_EXPIRATION_BUFFER",
		"cleanup_interval":            "MCP_AUTH_CLEANUP_INTERVAL",
		"log_level":                   "MCP_AUTH_LOG_LEVEL",
		"enable_debug_logs":           "MCP_AUTH_ENABLE_DEBUG_LOGS",
	}
}

// GetConfig returns the current configuration (thread-safe)
func (acm *AuthConfigManager) GetConfig() *DynamicAuthConfig {
	acm.mutex.RLock()
	defer acm.mutex.RUnlock()
	
	// Return a copy to prevent external modifications
	configCopy := *acm.config
	return &configCopy
}

// UpdateConfig updates the configuration and validates it
func (acm *AuthConfigManager) UpdateConfig(newConfig *DynamicAuthConfig) error {
	// Validate the new configuration
	if err := newConfig.Validate(); err != nil {
		return fmt.Errorf("configuration validation failed: %w", err)
	}
	
	acm.mutex.Lock()
	defer acm.mutex.Unlock()
	
	// Store the old config for rollback if needed
	oldConfig := acm.config
	acm.config = newConfig
	
	// Notify all registered callbacks
	for _, callback := range acm.callbacks {
		func() {
			defer func() {
				if r := recover(); r != nil {
					// If a callback panics, log it but don't fail the update
					fmt.Printf("Configuration callback panicked: %v\n", r)
				}
			}()
			callback(newConfig)
		}()
	}
	
	// If we get here, the update was successful
	_ = oldConfig // Keep reference to avoid unused variable warning
	return nil
}

// RegisterCallback registers a callback function to be called when configuration changes
func (acm *AuthConfigManager) RegisterCallback(callback func(*DynamicAuthConfig)) {
	acm.mutex.Lock()
	defer acm.mutex.Unlock()
	
	acm.callbacks = append(acm.callbacks, callback)
}

// loadFromEnvironment loads configuration from environment variables
func (acm *AuthConfigManager) loadFromEnvironment() {
	config := GetDefaultDynamicAuthConfig()
	
	// Load enabled flag
	if envVal := os.Getenv(acm.envMappings["enabled"]); envVal != "" {
		if enabled, err := strconv.ParseBool(envVal); err == nil {
			config.Enabled = enabled
		}
	}
	
	// Load max auth states per session
	if envVal := os.Getenv(acm.envMappings["max_auth_states_per_session"]); envVal != "" {
		if maxStates, err := strconv.Atoi(envVal); err == nil && maxStates > 0 {
			config.MaxAuthStatesPerSession = maxStates
		}
	}
	
	// Load token expiration buffer
	if envVal := os.Getenv(acm.envMappings["token_expiration_buffer"]); envVal != "" {
		if duration, err := time.ParseDuration(envVal); err == nil {
			config.TokenExpirationBuffer = duration
		}
	}
	
	// Load cleanup interval
	if envVal := os.Getenv(acm.envMappings["cleanup_interval"]); envVal != "" {
		if duration, err := time.ParseDuration(envVal); err == nil {
			config.CleanupInterval = duration
		}
	}
	
	// Load log level
	if envVal := os.Getenv(acm.envMappings["log_level"]); envVal != "" {
		config.LogLevel = strings.ToLower(envVal)
	}
	
	// Load debug logs flag
	if envVal := os.Getenv(acm.envMappings["enable_debug_logs"]); envVal != "" {
		if debugLogs, err := strconv.ParseBool(envVal); err == nil {
			config.EnableDebugLogs = debugLogs
		}
	}
	
	// Validate and update the configuration
	if err := acm.UpdateConfig(config); err != nil {
		// If validation fails, keep the default config
		fmt.Printf("Failed to load configuration from environment: %v\n", err)
	}
}

// ReloadFromEnvironment reloads configuration from environment variables
func (acm *AuthConfigManager) ReloadFromEnvironment() error {
	acm.loadFromEnvironment()
	return nil
}

// LoadFromJSON loads configuration from JSON data
func (acm *AuthConfigManager) LoadFromJSON(data []byte) error {
	// First unmarshal into a map to handle duration strings
	var rawConfig map[string]interface{}
	if err := json.Unmarshal(data, &rawConfig); err != nil {
		return fmt.Errorf("failed to unmarshal configuration: %w", err)
	}
	
	// Start with default config
	config := GetDefaultDynamicAuthConfig()
	
	// Parse each field individually
	if enabled, ok := rawConfig["enabled"].(bool); ok {
		config.Enabled = enabled
	}
	
	if maxStates, ok := rawConfig["max_auth_states_per_session"].(float64); ok {
		config.MaxAuthStatesPerSession = int(maxStates)
	}
	
	if tokenBuffer, ok := rawConfig["token_expiration_buffer"].(string); ok {
		if duration, err := time.ParseDuration(tokenBuffer); err == nil {
			config.TokenExpirationBuffer = duration
		} else {
			return fmt.Errorf("invalid token_expiration_buffer duration: %s", tokenBuffer)
		}
	}
	
	if cleanupInterval, ok := rawConfig["cleanup_interval"].(string); ok {
		if duration, err := time.ParseDuration(cleanupInterval); err == nil {
			config.CleanupInterval = duration
		} else {
			return fmt.Errorf("invalid cleanup_interval duration: %s", cleanupInterval)
		}
	}
	
	if logLevel, ok := rawConfig["log_level"].(string); ok {
		config.LogLevel = logLevel
	}
	
	if debugLogs, ok := rawConfig["enable_debug_logs"].(bool); ok {
		config.EnableDebugLogs = debugLogs
	}
	
	return acm.UpdateConfig(config)
}

// ToJSON exports the current configuration as JSON
func (acm *AuthConfigManager) ToJSON() ([]byte, error) {
	config := acm.GetConfig()
	return json.MarshalIndent(config, "", "  ")
}

// IsEnabled returns whether dynamic authentication is enabled
func (acm *AuthConfigManager) IsEnabled() bool {
	config := acm.GetConfig()
	return config.Enabled
}

// GetLogLevel returns the current log level
func (acm *AuthConfigManager) GetLogLevel() string {
	config := acm.GetConfig()
	return config.LogLevel
}

// IsDebugEnabled returns whether debug logging is enabled
func (acm *AuthConfigManager) IsDebugEnabled() bool {
	config := acm.GetConfig()
	return config.EnableDebugLogs
}

// GetMaxAuthStatesPerSession returns the maximum number of auth states per session
func (acm *AuthConfigManager) GetMaxAuthStatesPerSession() int {
	config := acm.GetConfig()
	return config.MaxAuthStatesPerSession
}

// GetTokenExpirationBuffer returns the token expiration buffer duration
func (acm *AuthConfigManager) GetTokenExpirationBuffer() time.Duration {
	config := acm.GetConfig()
	return config.TokenExpirationBuffer
}

// GetCleanupInterval returns the cleanup interval duration
func (acm *AuthConfigManager) GetCleanupInterval() time.Duration {
	config := acm.GetConfig()
	return config.CleanupInterval
}

// ValidateEnvironmentVariables validates all environment variables without applying them
func (acm *AuthConfigManager) ValidateEnvironmentVariables() error {
	var errors []string
	
	// Validate enabled flag
	if envVal := os.Getenv(acm.envMappings["enabled"]); envVal != "" {
		if _, err := strconv.ParseBool(envVal); err != nil {
			errors = append(errors, fmt.Sprintf("invalid %s: %s", acm.envMappings["enabled"], envVal))
		}
	}
	
	// Validate max auth states per session
	if envVal := os.Getenv(acm.envMappings["max_auth_states_per_session"]); envVal != "" {
		if maxStates, err := strconv.Atoi(envVal); err != nil || maxStates <= 0 {
			errors = append(errors, fmt.Sprintf("invalid %s: %s (must be positive integer)", acm.envMappings["max_auth_states_per_session"], envVal))
		}
	}
	
	// Validate token expiration buffer
	if envVal := os.Getenv(acm.envMappings["token_expiration_buffer"]); envVal != "" {
		if _, err := time.ParseDuration(envVal); err != nil {
			errors = append(errors, fmt.Sprintf("invalid %s: %s (must be valid duration)", acm.envMappings["token_expiration_buffer"], envVal))
		}
	}
	
	// Validate cleanup interval
	if envVal := os.Getenv(acm.envMappings["cleanup_interval"]); envVal != "" {
		if duration, err := time.ParseDuration(envVal); err != nil || duration <= 0 {
			errors = append(errors, fmt.Sprintf("invalid %s: %s (must be positive duration)", acm.envMappings["cleanup_interval"], envVal))
		}
	}
	
	// Validate log level
	if envVal := os.Getenv(acm.envMappings["log_level"]); envVal != "" {
		validLogLevels := map[string]bool{
			"debug": true,
			"info":  true,
			"warn":  true,
			"error": true,
		}
		if !validLogLevels[strings.ToLower(envVal)] {
			errors = append(errors, fmt.Sprintf("invalid %s: %s (valid levels: debug, info, warn, error)", acm.envMappings["log_level"], envVal))
		}
	}
	
	// Validate debug logs flag
	if envVal := os.Getenv(acm.envMappings["enable_debug_logs"]); envVal != "" {
		if _, err := strconv.ParseBool(envVal); err != nil {
			errors = append(errors, fmt.Sprintf("invalid %s: %s", acm.envMappings["enable_debug_logs"], envVal))
		}
	}
	
	if len(errors) > 0 {
		return fmt.Errorf("environment variable validation failed: %s", strings.Join(errors, "; "))
	}
	
	return nil
}

// GetEnvironmentVariableHelp returns help text for environment variables
func (acm *AuthConfigManager) GetEnvironmentVariableHelp() string {
	help := `Dynamic Authentication Configuration Environment Variables:

MCP_DYNAMIC_AUTH_ENABLED (bool)
    Enable or disable dynamic authentication functionality
    Default: true
    Example: MCP_DYNAMIC_AUTH_ENABLED=false

MCP_AUTH_MAX_STATES_PER_SESSION (int)
    Maximum number of authentication states per session
    Default: 10
    Example: MCP_AUTH_MAX_STATES_PER_SESSION=20

MCP_AUTH_TOKEN_EXPIRATION_BUFFER (duration)
    Buffer time before token expiration to consider it expired
    Default: 5m
    Example: MCP_AUTH_TOKEN_EXPIRATION_BUFFER=10m

MCP_AUTH_CLEANUP_INTERVAL (duration)
    Interval for cleaning up expired authentication states
    Default: 10m
    Example: MCP_AUTH_CLEANUP_INTERVAL=5m

MCP_AUTH_LOG_LEVEL (string)
    Log level for authentication operations
    Valid values: debug, info, warn, error
    Default: info
    Example: MCP_AUTH_LOG_LEVEL=debug

MCP_AUTH_ENABLE_DEBUG_LOGS (bool)
    Enable detailed debug logging for authentication
    Default: false
    Example: MCP_AUTH_ENABLE_DEBUG_LOGS=true
`
	return help
}